/*
- Malt Project - read.cpp
- This document is protected by the GPL protocol.
- Writer:@Index-Flandre
*/

#include <vector>
#include <list>
#include "../../include/object/object.hpp"
#include "../../include/object/error.hpp"
#include "../../include/object/number.hpp"
#include "../../include/object/array.hpp"
#include "../../include/object/string.hpp"
#include "../../include/object/list.hpp"

namespace malt
{

bool is_repl_mode;
unsigned long line, ch_size;

struct BoxObjEnd : public Number
{
} File_end, Array_end, List_end;

Object *m_read(FILE *stream);

Object *structing_list(FILE *stream)
{
	List *ls = (List *)NewList();
	for (Object *ob = m_read(stream); ob != &List_end;)
	{
		if (ob == &Array_end)
		{
			char serr[] = "default of escape char";
			char *ss = CopyStr(serr, sizeof(serr));
			SyntexError.ErrBody = (String *)NewString(ss, sizeof(serr));
			merrno = &SyntexError;
			throw SyntexError;
		}
		if (ob != MaltNullptr)
			ls->append(ob);
		ls->size++;
		ob = m_read(stream);
	}
	return ls;
}

char Escape_char(char c)
{
	switch (c)
	{
	case '0':
		return '\0';
	case 'a':
		return '\a';
	case 'b':
		return '\b';
	case 'e':
		return '\e';
	case 'f':
		return '\f';
	case 'r':
		return '\r';
	case 't':
		return '\t';
	case 'n':
		return '\n';
	case '\n':
		line++;
		return '\n';
	case '\\':
		return '\\';
	case '\"':
		return '\"';
	default:
		char serr[] = "default of escape char";
		char *ss = CopyStr(serr, sizeof(serr));
		SyntexError.ErrBody = (String *)NewString(ss, sizeof(serr));
		merrno = &SyntexError;
		throw SyntexError;
	}
}

Object *structing_string(FILE *stream)
{
	std::vector<char> str;
	bool is_exit = false;
	for (; !is_exit;)
	{
		char ch = getc(stream);
		ch_size++;
		switch (ch)
		{
		case '\"':
			is_exit = true;
			break;
		case '\\':
			str.push_back(Escape_char(getc(stream)));
			break;
		case '\n':
			line++;
			ch_size = 0;
		case EOF:
		{
			char serr[] = "default of escape char";
			char *ss = CopyStr(serr, sizeof(serr));
			SyntexError.ErrBody = (String *)NewString(ss, sizeof(serr));
			merrno = &SyntexError;
			throw SyntexError;
		}
		default:
			str.push_back(ch);
			break;
		}
	}
	if (str.size() == 0)
		return &NullString;
	String *mstr = (String *)NewString();
	mstr->size = str.size();
	mstr->chars = new char[mstr->size];
	for (unsigned long i = 0; i < mstr->size; i++)
	{
		mstr->chars[i] = str[i];
	}
	return mstr;
}

Object *structing_array(FILE *stream)
{
	std::vector<Object *> vecarr;
	for (Object *ob = m_read(stream); ob != &Array_end;)
	{
		if (ob == &Array_end)
		{
			char serr[] = "default of escape char";
			char *ss = CopyStr(serr, sizeof(serr));
			SyntexError.ErrBody = (String *)NewString(ss, sizeof(serr));
			merrno = &SyntexError;
			throw SyntexError;
		}
		if (ob != MaltNullptr)
			vecarr.push_back(ob);
		ob = m_read(stream);
	}
	Array *arr = (Array *)NewArray();
	arr->size = vecarr.size();
	arr->table = new Object *[arr->size];
	for (unsigned long sz = 0; sz < arr->size; sz++)
	{
		arr->table[sz] = vecarr[sz];
	}
	return arr;
}

Object *structing_quote(FILE *stream = stdin)
{
	return m_read(stream);
}

bool Is_Number(std::vector<char> *vo)
{
	size_t _a = 0, _b = 0;
	if ((*vo)[vo->size() - 1] == '-' || (*vo)[vo->size() - 1] == '.')
		return false;
	for (size_t i = 0; i < vo->size(); i++)
	{
		if ((*vo)[i] == '-')
		{
			_a++;
		}
		else if ((*vo)[i] == '.')
		{
			_b++;
		}
		else if (!((*vo)[i] >= '0' && (*vo)[i] <= '9'))
		{
			return false;
		}
	}
	if (_a > 1 || _b > 1)
	{
		return false;
	}
	return true;
}

#define _STR_TO_BOX_OBJ(X)                                \
	if (read_char_cache.size() != 0)                        \
	{                                                       \
		if (Is_Number(&read_char_cache))                      \
		{                                                     \
			char *numstr = new char[read_char_cache.size()];    \
			for (size_t i = 0; i < read_char_cache.size(); i++) \
			{                                                   \
				numstr[i] = read_char_cache[i];                   \
			}                                                   \
			Number *num = (Number *)NewNumber();                \
			sscanf(numstr, "%lf", &num->val);                   \
			read_obj_cache.push_back(&X);                       \
			return num;                                         \
		}                                                     \
		else                                                  \
		{                                                     \
			char *sstr = new char[read_char_cache.size()];      \
			for (size_t i = 0; i < read_char_cache.size(); i++) \
			{                                                   \
				sstr[i] = read_char_cache[i];                     \
			}                                                   \
			Symbol *sym = (Symbol *)NewSymbol();                \
			sym->size = read_char_cache.size();                 \
			sym->chars = sstr;                                  \
			read_obj_cache.push_back(&X);                       \
			return sym;                                         \
		}                                                     \
	}

#define _STR_TO_OBJ                                       \
	if (read_char_cache.size() != 0)                        \
	{                                                       \
		if (Is_Number(&read_char_cache))                      \
		{                                                     \
			char *numstr = new char[read_char_cache.size()];    \
			for (size_t i = 0; i < read_char_cache.size(); i++) \
			{                                                   \
				numstr[i] = read_char_cache[i];                   \
			}                                                   \
			Number *num = (Number *)NewNumber();                \
			sscanf(numstr, "%lf", &num->val);                   \
			read_obj_cache.push_back(num);                      \
		}                                                     \
		else                                                  \
		{                                                     \
			char *sstr = new char[read_char_cache.size()];      \
			for (size_t i = 0; i < read_char_cache.size(); i++) \
			{                                                   \
				sstr[i] = read_char_cache[i];                     \
			}                                                   \
			Symbol *sym = (Symbol *)NewSymbol();                \
			sym->size = read_char_cache.size();                 \
			sym->chars = sstr;                                  \
			read_obj_cache.push_back(sym);                      \
		}                                                     \
	}

#define XDD_ERR                                                  \
	if (read_char_cache.size() > 0)                                \
	{                                                              \
		char serr[] = "元素之间要加空格";                            \
		char *ss = CopyStr(serr, sizeof(serr));                      \
		SyntexError.ErrBody = (String *)NewString(ss, sizeof(serr)); \
		merrno = &SyntexError;                                       \
		throw SyntexError;                                           \
	}

std::vector<Object *> read_obj_cache;

extern Object *m_read(FILE *stream = stdin)
{
	/*
	if (read_obj_cache.size() >= 1)
	{
		Object *obj = read_obj_cache[0];
		std::vector<Object *>::iterator iters = read_obj_cache.begin();
		read_obj_cache.erase(iters);
		return obj;
	}
	*/
	std::vector<char> read_char_cache;
	for (char ch = 0; ch != EOF;)
	{
		ch = getc(stream);
		ch_size++;
		switch (ch)
		{
		case '(':
		{
			XDD_ERR;
			return structing_list(stream);
		}
		case ')':
			_STR_TO_BOX_OBJ(List_end);
			return &List_end;
		case '{':
			XDD_ERR;
			return structing_array(stream);
		case '}':
			_STR_TO_BOX_OBJ(Array_end);
			return &Array_end;
		case '\"':
			XDD_ERR;
			return structing_string(stream);
		case '\'':
			return structing_quote(stream);
		case ' ':
		case '\t':
		{
			_STR_TO_OBJ;
			return MaltNullptr;
		}
		case '\n':
		{
			line++;
			ch_size = 0;
			_STR_TO_OBJ;
			return MaltNullptr;
		}
		default:
			read_char_cache.push_back(ch);
			break;
		}
	}
	return &File_end;
}

Object *__read__(FILE *stream)
{
	Object *obj = m_read(stream);
	List *ls = (List *)MaltNullptr;
	if (obj != MaltNullptr)
	{
		return obj;
	}
	else
	{
		if (read_obj_cache.size() == 0)
		{
			return MaltNullptr;
		}
		else if (read_obj_cache.size() == 1)
		{
			Object *ob1 = read_obj_cache[0];
			read_obj_cache.clear();
			return ob1;
		}
		else
		{
			ls = (List *)NewList();
			for (unsigned long i = 0; i < read_obj_cache.size(); i++)
			{
				ls->append(read_obj_cache[i]);
			}
			read_obj_cache.clear();
			return ls;
		}
	}
}
}